<?php
/*
 * Quark PHP Framework
 * Copyright 2011, Sahib Alejandro Jaramillo Leo.
 * http://quarkphp.com/
 * Licensed under the GNU General Public License (http://www.gnu.org/licenses/gpl.html).
 */
/**
 * Definicion de clase base Controller
 * @filesource
 */
/**
 * Clase base Controller
 * @author sahib
 */
abstract class Controller {

	/**
	 * Instance de clase Lang
	 * @access public
	 * @see Quark::configEnv()
	 * @var Lang
	 */
	public $Lang;

	/**
	 * Instancia de la View
	 * @access protected
	 * @var View
	 */
	protected $View;

	/**
	 * Instancia de AjaxResponse
	 * @access protected
	 * @var AjaxResponse
	 */
	protected $Ajax;

	/**
	 * Array de objetos instanciados con "createInstance"
	 * @access protected
	 * @var array
	 */
	protected $loaded_objects;

	/**
	 * Array asociativo con la lista de niveles de acceso para los actions
	 * @access protected
	 * @var array
	 */
	protected $custom_access_level;

	/**
	 * Nivel de acceso por default para los actions
	 * Por defecto es 0 (publico)
	 * @access protected
	 * @var int
	 */
	protected $default_access_level;

	/**
	 * Instancia de Css
	 * @access public
	 * @see Quark::configEnv()
	 * @var Css
	 */
	public $Css;

	/**
	 * Instancia de Js
	 * @access public
	 * @see Quark::configEnv()
	 * @var Js
	 */
	public $Js;

	/**
	 * Instancia de SignedUser
	 * @access public
	 * @see Quark::configEnv()
	 * @var SignedUser
	 */
	public $SignedUser;

	/**
	 * Nombre del controlador en ejecucion
	 * @access protected
	 * @var string
	 */
	protected static $running_controller_name;

	/**
	 * Nombre de la accion en ejecucion
	 * @access protected
	 * @var string
	 */
	protected static $running_action_name;

	/**
	 * Nombre del controller en ejecucion como se solicito en la URL
	 * @access protected
	 * @var string
	 */
	protected static $running_controller_url_name;

	/**
	 * Nombre del action en ejecucion como se solicito en la URL
	 * @access protected
	 * @var string
	 */
	protected static $running_action_url_name;

	/**
	 * Nombre del controlador solicitado
	 * @var string
	 */
	protected static $requested_controller_name;

	/**
	 * Nombre del action solicitado
	 * @var string
	 */
	protected static $requested_action_name;

	/**
	 * Nombre del controlador solicitado con formato URL
	 * @var string
	 */
	protected static $requested_controller_url_name;

	/**
	 * Nombre del action solicitado con formato URL
	 * @var string
	 */
	protected static $requested_action_url_name;

	/**
	 * Lista de nombres de clases para usar con "createInstance" automaticamente.
	 * @access protected
	 * @var array
	 */
	protected static $auto_instances = array();

	/**
	 * @deprecated
	 * @see Controller::$Ajax
	 */
	protected $AjaxResponse;

	/**
	 * Constructor
	 * @access public
	 * @return Controller
	 */
	public function __construct( ) {

		$this->View = new View( $this );
		$this->default_access_level = 0;
		$this->loaded_objects = array();
		$this->custom_access_level = array();
		
		// Instanciar automaticamente las clases especificadas en la variable de configuracion "auto_instance".
		foreach( self::$auto_instances as $class_name )
			$this->createInstance( $class_name );
		
		// Mantener compatibilidad con versiones <1.0.x
		$this->AjaxResponse = $this->Ajax;
	}

	/**
	 * Define los nombres de clases para auto instance
	 * @access public
	 * @param array $auto_instances lista de nombres
	 */
	public static function setAutoInstances( $auto_instances ) {

		self::$auto_instances = $auto_instances;
	}

	/**
	 * Fixea un nombre de controller o action, desde la URL para convertirlo a nombre valido para PHP
	 * @param string $name nombre
	 * @param bool $is_action TRUE si es accion, por defecto FALSE
	 * @return nombre fixeado
	 */
	private static function fixName( $name_url_format, $is_action = FALSE ) {

		if( $is_action == FALSE ) {
			$name_url_format = ucfirst( $name_url_format );
		}
		if( strpos( $name_url_format, '-' ) !== FALSE ) {
			$parts = array_map( 'ucfirst', explode( '-', $name_url_format ) );
			if( $is_action )
				$parts[0] = strtolower( $parts[0] );
			$name_url_format = implode( '', $parts );
		}
		return $name_url_format;
	}

	/**
	 * Devuelve el nombre del controlador en ejecucion
	 * @access public
	 * @param bool $from_url formato url
	 * @return string nombre del controlador
	 */
	public static function getRunningControllerName( $url_format = FALSE ) {

		return $url_format ? self::$running_controller_url_name : self::$running_controller_name;
	}

	/**
	 * Devuelve el nombre de la accion en ejecucion
	 * @access public
	 * @param bool $from_url formato url
	 * @return string nombre de la accion
	 */
	public static function getRunningActionName( $url_format = FALSE ) {

		return $url_format ? self::$running_action_url_name : self::$running_action_name;
	}

	/**
	 * Establece el nombre del controlador en ejecucion
	 * @access public
	 * @param string $url_name nombre del controlador en formato url
	 */
	public static function setRunningControllerName( $name_url_format ) {

		self::$running_controller_url_name = $name_url_format;
		self::$running_controller_name = self::fixName( $name_url_format );
	}

	/**
	 * Establece el nombre de la accion en ejecucion
	 * @access public
	 * @param string $url_name nombre de la accion en formato url
	 */
	public static function setRunningActionName( $name_url_format ) {

		self::$running_action_url_name = $name_url_format;
		self::$running_action_name = self::fixName( $name_url_format, TRUE );
	}

	/**
	 * @return string
	 */
	public static function getRequestedControllerName( $url_format = FALSE ) {

		return $url_format ? self::$requested_controller_url_name : self::$requested_controller_name;
	}

	/**
	 * @return string
	 */
	public static function getRequestedActionName( $url_format = FALSE ) {

		return $url_format ? self::$requested_action_url_name : self::$requested_action_name;
	}

	/**
	 * @param string $name_url_format
	 */
	public static function setRequestedControllerName( $name_url_format ) {

		self::$requested_controller_url_name = $name_url_format;
		self::$requested_controller_name = self::fixName( $name_url_format );
	}

	/**
	 * @param string $name_url_format
	 */
	public static function setRequestedActionName( $name_url_format ) {

		self::$requested_action_url_name = $name_url_format;
		self::$requested_action_name = self::fixName( $name_url_format, TRUE );
	}

	/**
	 * Instancia un objeto $class_name directamente a una propiedad en el controlador.
	 * Se puede enviar un numero n de argumentos despues de $class_name, estos seron enviados al constructor de la clase.
	 * @access protected
	 * @param string $class_name Nombre del objeto que se quiere instanciar
	 * @return Controller
	 */
	protected function createInstance( $class_name ) {

		if( !isset( $this->loaded_objects[$class_name] ) ) {
			
			$args = func_get_args( );
			
			if( count( $args ) == 1 )
				$this->loaded_objects[$class_name] = new $class_name( );
			
		// Crear instruccion para instanciar objeto pasando parametros al constructor.
			else
				$this->loaded_objects[$class_name] = eval( "return new  $class_name(" . args2str( array_slice( $args, 1 ) ) . ");" );
			
		// Sobrecargar controller y vista, para dar mos flexibilidad.
			$this->$class_name = $this->loaded_objects[$class_name];
			$this->View->$class_name = $this->loaded_objects[$class_name];
			
			// Mantener referencia entre el nuevo objeto y el controller.
			$this->$class_name->Controller = $this;
		}
		return $this;
	}

	/**
	 * Devuelve el nivel de acceso para al action $action.
	 * Si no se especifica $action, se asume que sera
	 * el nombre del action en ejecucion.
	 * @access public
	 * @param string $action Nombre de la accion (metodo)
	 * @return int
	 */
	public function getActionAccessLevel( $action = NULL ) {

		$action = ($action == NULL ? self::getRunningActionName( ) : $action);
		
		if( !isset( $this->custom_access_level[$action] ) )
			return $this->default_access_level;
		else
			return $this->custom_access_level[$action];
	}

	/**
	 * Establece el nivel de acceso por defecto para todos los actions (metdos)
	 * @access protected
	 * @param int $access_level Nivel de acceso
	 */
	protected function setDefaultAccessLevel( $access_level ) {

		$this->default_access_level = $access_level;
	}

	/**
	 * Establece el nivel de acceso para cada action (metodo) del controlador.
	 * @access protected
	 * @param array $array Array asociativo de niveles, con formato 'actionNameOne' => nivel, 'actionNameTwo' => nivel2, etc.
	 */
	protected function setCustomAccessLevel( $array ) {

		$this->custom_access_level = array_merge( $this->custom_access_level, $array );
	}

	/**
	 * Muestra un mensaje generico de "Acceso denegado"
	 * @access public
	 */
	public function accessDenied( ) {

		if( Ajax::onAjaxMode( ) == FALSE )
			echo '<html><head><title>Acceso denegado.</title></head><body>Acceso denegado.<br><a href="' . Url::getBaseUrl( ) .
				 '">Inicio</a></body></html>';
		else {
			$this->Ajax->setError( 'Acceso denegado' );
			$this->Ajax->send( );
		}
	}

	/**
	 * Esta acción se invoca cuando se intenta acceder a una acción no definida en el controlador.
	 * @throws Exception
	 */
	public function noActionReceptor( ) {

		throw new Exception( 'Acción "' . Controller::getRequestedActionName( ) . '" no encontrada' );
	}
}